package com.hm.health.View;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

import com.goodix.utils.DensityUtil;
import com.goodix.utils.TimeUtil;
import com.hm.health.R;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created by chenshi on 2019/12/25.
 */



public class ECGChartView extends View {

    public static final String TAG = "ECGChartView";

    private static final int DEFAULT_SIZE = 300;
    public static final int DEFAULT_FREQUENCY = 500;

    private Paint mPaint;
    private Paint mTextPaint;
    private Paint m_gridLinePainter;
    private Paint mLinePaint;
    private int m_gridColor;
    private int m_sGridColor;
    private int mLineColor;
    private int m_backgroundColor;
    private int mTextColor;
    private ArrayList<Float> mDatas = new ArrayList<>();
    private List<Integer> mLeadState = new ArrayList<>();
    private int m_currentIndex;
    private float m_max;
    private float m_min;
    private int m_frequency;
    private float mTextSize;
    private float mLineWidth;
    private int mDataIndex;
    private boolean mShowLeftAxis;

    private RectF mEcgChartRect;
    private RectF leftAxisRect;
    private RectF bottomAxisRect;
    private int marginTop;
    private int marginLeft;
    private int marginBottom;
    private int marginRight;

    private boolean mShowFormatTime;
    private long mFirstTimestamp;

    public static final float DEFAULT_MAX = 2f;
    public static final float DEFAULT_MIN = -1.5f;

    public static final float INVALID_ECG = -5f;
    private int m_duration = 3;
    private Paint mLeftAxisPaint;
    private int mPlotHeight;
    private Rect mTextRect = new Rect();

    public ECGChartView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ECGChartView);
        m_backgroundColor = typedArray.getColor(R.styleable.ECGChartView_back_ground_color, Color.rgb(10,15,20));
        m_gridColor = typedArray.getColor(R.styleable.ECGChartView_grid_line_color, Color.rgb(0x60, 0xE9, 0xAA));
        m_sGridColor = typedArray.getColor(R.styleable.ECGChartView_s_grid_line_color, Color.rgb(0x20, 0x50, 0x3A));
        mLineColor = typedArray.getColor(R.styleable.ECGChartView_line_color, Color.RED);
        mTextColor = typedArray.getColor(R.styleable.ECGChartView_text_color, Color.BLACK);
        mTextSize = typedArray.getDimension(R.styleable.ECGChartView_text_size, DensityUtil.dip2px(getContext(), 8));
        mLineWidth = typedArray.getDimension(R.styleable.ECGChartView_line_width, 1);
        mShowLeftAxis = typedArray.getBoolean(R.styleable.ECGChartView_show_left_axis, true);
        m_duration = typedArray.getInteger(R.styleable.ECGChartView_duration, 0);
        typedArray.recycle();

        if (m_duration > 0) {
            setFrequency(m_duration, DEFAULT_FREQUENCY);
        }
        init();
    }

    public void setFrequency(int duration, int frequency) {
        m_frequency = frequency;
        m_duration = duration;
        mDatas.clear();
        mLeadState.clear();
        for (int i = 0; i < duration * frequency; ++i) {
            mDatas.add(INVALID_ECG);
            mLeadState.add(-1);
        }
    }

    public void setLineWidth(int lineWidth) {
        mLineWidth = lineWidth;
        mLinePaint.setStrokeWidth(mLineWidth);
    }

    public void setShowFormatTime(boolean isShow) {
        mShowFormatTime = isShow;
    }

    public void setFirstTimestamp(long timestamp) {
        mFirstTimestamp = timestamp;
    }

    protected void init() {

        mEcgChartRect = new RectF();
        leftAxisRect = new RectF();
        bottomAxisRect = new RectF();

        m_frequency = 100;
        m_max = DEFAULT_MAX;
        m_min = DEFAULT_MIN;

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(m_backgroundColor);
        mPaint.setStrokeWidth(1);

        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStyle(Paint.Style.STROKE);
        mLinePaint.setColor(mLineColor);
        mLinePaint.setStrokeWidth(mLineWidth);

        mLeftAxisPaint = new Paint();
        mLeftAxisPaint.setAntiAlias(true);
        mLeftAxisPaint.setStyle(Paint.Style.STROKE);
        mLeftAxisPaint.setColor(mTextColor);
        mLeftAxisPaint.setStrokeWidth(1);

        m_gridLinePainter = new Paint();
        m_gridLinePainter.setAntiAlias(true);
        m_gridLinePainter.setStyle(Paint.Style.FILL_AND_STROKE);
        m_gridLinePainter.setColor(m_gridColor);
        m_gridLinePainter.setStrokeWidth(1);

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);

        m_currentIndex = 0;

        marginTop = DensityUtil.dip2px(getContext(), 4);
        marginLeft = DensityUtil.dip2px(getContext(), 4);
        marginRight = DensityUtil.dip2px(getContext(), 4);
        marginBottom = DensityUtil.dip2px(getContext(), 4);
        String yLabel = "1.0s";
        mTextPaint.getTextBounds(yLabel, 0, yLabel.length(), mTextRect);

        mShowFormatTime = false;
        mFirstTimestamp = 0;
    }

    private int getMinHeight(int width) {
        float yAxisLabelWidth = DensityUtil.dip2px(getContext(), 10);

        float fontHeight = mTextPaint.descent() - mTextPaint.ascent();

        float xAxisLabelHeight = fontHeight + 2;

        float plotWidth = width - marginLeft - marginRight - yAxisLabelWidth;

        int columnCnt = m_duration * 5;
        float oGridWidth = plotWidth / columnCnt;
        int rowCount = (int) ((m_max - m_min) * 2);

        float plotBottom = rowCount * oGridWidth + marginTop;

        return (int) (plotBottom + xAxisLabelHeight);
    }

    protected void onDraw(Canvas canvas) {

        float yAxisLabelWidth = DensityUtil.dip2px(getContext(), 10);

        float fontHeight = mTextPaint.descent() - mTextPaint.ascent();

        float xAxisLabelHeight = fontHeight + 2;

        float plotWidth = getWidth() - marginLeft - marginRight - yAxisLabelWidth;

        int columnCnt = m_duration * 5;
        float oGridWidth = plotWidth / columnCnt;
        float sGridWidth = oGridWidth / 5;
        int rowCount = (int) ((m_max - m_min) * 2);

        float plotBottom = rowCount * oGridWidth + marginTop;

        // 左侧Y轴
        leftAxisRect.set(marginLeft, marginTop,
                yAxisLabelWidth + marginLeft,plotBottom);

        // ECG 曲线区域
        mEcgChartRect.set(leftAxisRect.right, leftAxisRect.top,
                leftAxisRect.right + plotWidth, leftAxisRect.bottom);

        // 底部X轴
        bottomAxisRect.set(leftAxisRect.right, leftAxisRect.bottom,
                mEcgChartRect.right, mEcgChartRect.bottom + xAxisLabelHeight);

        drawGrid(canvas, mEcgChartRect, rowCount * 5, columnCnt * 5);

        mPlotHeight = (int) bottomAxisRect.bottom;

        if (mShowLeftAxis) {
            drawYAxisScale(canvas, leftAxisRect, sGridWidth);
        }

        drawXAxisScale(canvas, bottomAxisRect, columnCnt, sGridWidth);

        drawEcgLine(canvas, mEcgChartRect, sGridWidth);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int wSize = MeasureSpec.getSize(widthMeasureSpec);
        if (0 != wSize) {
            int height = getMinHeight(wSize);
            setMeasuredDimension(wSize, height);
//            Log.d(TAG, "onMeasure: " + height + ", " + wSize);
        }
    }

    // 获取lead状态
    private int getLeadState(int index, int oldLeadState) {
        int lead = 0;
        if (index < mLeadState.size()) {
            lead = mLeadState.get(index);
        }

        if (lead == 1) {
            // lead on
        } else if (lead == 2) {
            // lead off
        } else {
            // not change
            lead = oldLeadState;
        }

        return lead;
    }

    // 导出pdf页面绘图根据lead改变颜色
    private void changePainColor(int lead, int index) {
        if (lead == 1) {
            // lead on
            mLinePaint.setColor(mLineColor);
            Log.d(TAG, "change to color lead on at " + index);
        } else if (lead == 2) {
            // lead off
            mLinePaint.setColor(Color.BLACK);
            Log.d(TAG, "change to color lead off at " + index);
        } else {
            // not change
        }
    }

    private void drawEcgLine(Canvas canvas, RectF plotRect, float gridHeight) {

        float xCoef = (float)(plotRect.width() / (mDatas.size() * 1.0));
        float yZero = gridHeight * m_max * 10 + plotRect.top;
        if(mDatas.size() > 1)
        {
            Path mPath = new Path();

            float v = 0;
            int prevLead = -1;

            for (int i = 0; i < mDatas.size(); ++i) {
                // change color by lead state
                int lead = getLeadState(i, prevLead);
                /*if (i==0) {
                    Log.d(TAG, "i==0 lead " + lead + ", prev lead " + prevLead);
                }*/

                // change color at lead change
                if (lead != prevLead) {
                    canvas.drawPath(mPath, mLinePaint);
                    prevLead = lead;
                    mPath.reset();
                    mPath.moveTo(plotRect.left + i * xCoef, yZero - v);
                    changePainColor(lead, i);
                }

                if ((mDatas.get(i) != INVALID_ECG) && ((i == 0) || ( (i > 0)  && (mDatas.get(i-1) == INVALID_ECG)))) {
                    // Start position
                    v = mDatas.get(i) * 10 * gridHeight;
                    mPath.moveTo(plotRect.left + i * xCoef, yZero - v);
                } else if ((i > 0) && (mDatas.get(i-1) != INVALID_ECG) && (mDatas.get(i) == INVALID_ECG)) {
                    // End position
                    canvas.drawPath(mPath, mLinePaint);
                }

                if (mDatas.get(i) != INVALID_ECG) {
                    v = mDatas.get(i) * 10 * gridHeight;
                    mPath.lineTo(plotRect.left + i * xCoef, 0 - v + yZero);
                }
            }

            if(mDatas.get(mDatas.size() - 1) != INVALID_ECG) {
                canvas.drawPath(mPath, mLinePaint);
            }
        }
    }

    private int dip2px(int dp) {
        return DensityUtil.dip2px(getContext(), dp);
    }

    private void drawYAxisScale(Canvas canvas, RectF plotRect, float gridHeight) {
        float yZero = gridHeight * m_max * 10 + plotRect.top;
        float x = plotRect.right - dip2px(4);
        Path path = new Path();

        path.moveTo(x, yZero);
        path.lineTo(x - dip2px(2), yZero);
        path.lineTo(x - dip2px(2), yZero - gridHeight * 10);
        path.lineTo(x - dip2px(6), yZero - gridHeight * 10);
        path.lineTo(x - dip2px(6), yZero);
        path.lineTo(x - dip2px(8), yZero);

        canvas.drawPath(path,mLeftAxisPaint);
    }

    void drawXAxisScale(Canvas canvas, RectF plotRect, int columnCount, float gridWidth) {
        for (int column = 0; column <= columnCount; column += 10) {
            canvas.drawLine(plotRect.left + column * gridWidth * 5 + 1,
                    plotRect.top,
                    plotRect.left + column * gridWidth * 5 + 1,
                    plotRect.top + mTextRect.height(), mTextPaint);

            // 不绘制最后一个标签
            if (column == columnCount) { continue;}

            StringBuilder xLab = new StringBuilder();
            if (mShowFormatTime && mFirstTimestamp != 0) {
                if (mDataIndex > mDatas.size()) {
                    int t = (mDataIndex - mDatas.size()) / (m_frequency / 10);
                    xLab.append(TimeUtil.formateEcgSecond(mFirstTimestamp + column * 200 + t * 100));
                } else {
                    xLab.append(TimeUtil.formateEcgSecond(mFirstTimestamp + column * 200));
                }
            } else {
                if (mDataIndex > mDatas.size()) {
                    int t = (mDataIndex - mDatas.size()) / (m_frequency / 10);
                    xLab.append(column * 0.2 + t * 0.1);
                } else {
                    xLab.append(column * 0.2);
                }
            }


            canvas.drawText(xLab.toString(),
                    plotRect.left + column * gridWidth * 5,
                    plotRect.top + mTextRect.height() + 1,
                    mTextPaint);
        }
    }

    private void drawGrid(Canvas canvas, RectF plotRect, float rowCount, float columnCnt) {

        m_gridLinePainter.setColor(m_backgroundColor);
        canvas.drawRect(plotRect, m_gridLinePainter);

        float gridWidth = plotRect.width() / columnCnt;
        float gridHeight = gridWidth;

        float right = plotRect.left + columnCnt * gridWidth;
        float bottom = plotRect.top + rowCount * gridHeight;

        // Draw column
        for (int col = 0; col <= columnCnt; ++col) {
            if (col % 5 == 0) {
                m_gridLinePainter.setStrokeWidth(2);
                m_gridLinePainter.setColor(m_gridColor);
            } else {
                m_gridLinePainter.setColor(m_sGridColor);
                m_gridLinePainter.setStrokeWidth(1);
            }

            float x = plotRect.left + col * gridWidth;

            canvas.drawLine(x, plotRect.top, x,bottom, m_gridLinePainter);
        }

        // Draw row
        for (int row = 0; row <= rowCount; ++row) {
            if (row % 5 == 0) {
                m_gridLinePainter.setStrokeWidth(2);
                m_gridLinePainter.setColor(m_gridColor);
            } else {
                m_gridLinePainter.setColor(m_sGridColor);
                m_gridLinePainter.setStrokeWidth(1);
            }

            float y = plotRect.top + row * gridHeight;
            canvas.drawLine(plotRect.left, y, right, y, m_gridLinePainter);
        }
    }

    public void addPointWithFlag(float data, int lead) {
        m_currentIndex = m_currentIndex % mDatas.size();

        mLeadState.set(m_currentIndex, lead);

        if (data > DEFAULT_MAX || data < DEFAULT_MIN) {
            mDatas.set(m_currentIndex++, INVALID_ECG);
        } else {
            mDatas.set(m_currentIndex++, data);
        }

        for (int i = 0; i < m_frequency / 50; ++i) {
            int index = (m_currentIndex + i) % mDatas.size();
            mDatas.set(index, INVALID_ECG);
            //mLeadState.set(index, -1);
        }
    }

    public void addPoint(float data) {
        addPointWithFlag(data, 0);
    }

    public void addPoint(float data, int index) {
        mDataIndex = index;
        addPoint(data);
    }

    public void addPointWithFlag(float data, int index, int lead) {
        mDataIndex = index;
        addPointWithFlag(data, lead);
    }

    public void clearData() {
        Collections.fill(mDatas, INVALID_ECG);
        Collections.fill(mLeadState, -1);
        m_currentIndex = 0;
        mDataIndex = 0;
    }
}

